#include <CommHeader.h>
#include <Comm.h>
#include <Random.h>
#include <QueueList.h>
#include <aJSON.h>

/* Read write buraya   */


/*-----------------------*/

//GLOBAL VARIABLES
struct PASSENGER {
  byte destination;
  byte src_station; 
  int timeStamp; // Arduinoda saat bilgisi tutulmuyor. Sadece açıldığı andan itibaren geçen süreyi mili saniye cinsinden tutuyor.
};
QueueList <PASSENGER> passengerQueue;
QueueList <PASSENGER> newPassengers;
char *json_String;
int type, dst, param_min, param_max;
int numOfCreatedPassenger;
int numOfGetPassenger;
float param_mean, param_stdv, param_lambda, param_probability;
int ST_ID;
boolean is_registered, flagMessage;
MESSAGE mes;

//FUNCTION PROTOTYPES
PASSENGER createPassenger(byte destination);
void addPassenger(byte, byte);
void stRead();
void stProcess();
char * createJsonString();
//SETUP
void setup(){
  Serial.begin(9600);
  delay(2000);
  is_registered = false;
  flagMessage= false;
  type=3;
  param_lambda=2;
  ST_ID=12;
  randomSeed(analogRead(0));
}

//LOOP
void loop(){
  delay(1000);
  ST_ID=0;
  addPassenger(6, 0);
  stWrite();
  /*addPassenger( 6, 0);//TEST
  int listSize= newPassengers.count();
  for(int i=0; i<listSize ; i++){
    char * updateParams = createJsonString();
    Serial.println(updateParams);//TEST
    Serial.flush();
    //delay(10000);
  }*/
  /*addPassenger(15, 2);
  char* plist;
  for(int i = 0 ; i<15 ; i++){
    plist=st_passengerlist(i);
    Serial.println(plist);
    Serial.println(i);
    Serial.flush();
    delay(250);
    free(plist);  
}*/
  /*
  if(! is_registered){//registered olmadıysak çalışır
    ready();
  }
  else{//registered olduysak çalışır
     stRead();  //bizim yazdığımız read
     stProcess();
     stWrite();  //coSend update 
  }*/
}

/*
 *Recieves messages and does server's commands, 
 *then sends appropriate an answer to the server
 */
void stRead(){
  if(coReceive(&mes)){
    switch(mes.cmd){
      case ST_SE_CONFIG:
        se_config();//parses json string for config
        coSend(OK,NULL);
        break;
    
      case ST_SE_ADD_PASSENGER:
        se_add_passenger();//parses json string for  addPassenger
        addPassenger(numOfCreatedPassenger, dst);
        coSend(OK,NULL);
        break;
 
      case ST_SE_GET_PASSENGER:
        se_get_passenger();//parses json string for getPassenger
        if(numOfGetPassenger > passengerQueue.count()){
          numOfGetPassenger = passengerQueue.count();
        }
        for(int i = 0 ; i<numOfGetPassenger ; i++){
          st_passengerlist(numOfGetPassenger);
          delay(300);
        }
        break;
    }
  }
}

/*
 *Generates random number according to type(distribution)
 */
void stProcess(){
  //serverdan bir şey geldiyse parse edilip aşağıdaki kod çalıştırılacak
  switch(type){
    case 1:
      numOfCreatedPassenger = Random::boundedUniform(param_min, param_max);
    break;
    case 2:
      numOfCreatedPassenger = Random::normal(param_mean, param_stdv);
    break;
    case 3:
      numOfCreatedPassenger = Random::exponential(param_lambda);
    break;
    case 4:
      numOfCreatedPassenger = Random::geometric(param_probability);
    break;
  }
  addPassenger( (byte) numOfCreatedPassenger, 0);
}


/*
 *Sends station info (like number of passengers in the station) to the server.
 */
void stWrite(){
  //json stringi yaratılacak.
  int listSize= newPassengers.count();
  char * updateParams;
  for(int i=0; i<listSize ; i++){
     updateParams = createJsonString();
     //coSend(ST_SE_UPDATE,updateParams);
     Serial.println(updateParams);
     free(updateParams);
  }
}

/*
 *station is ready
 */
void ready(){
  coSend(ST_SE_READY,NULL);
  delay(2000);
  if(coReceive(&mes)){
    if(mes.cmd == ST_SE_REGISTER){
      ST_ID = (int) mes.params; 
      coSend(OK,(char*)ST_ID);
      is_registered=true; 
    }
  }
}

/*
 *Creates passengers according to given number and destination.
 *Then adds them into queue
 */
void addPassenger(byte num, byte destination){
  int i=0;
  for(i=0; i<(int)num; i++){
     PASSENGER p = createPassenger(destination);
     passengerQueue.push(p);
     newPassengers.push(p);
   }
}
/*
 *Creates passengers with given destination
 */
PASSENGER createPassenger(byte destination){
     PASSENGER p;
     p.src_station=ST_ID;
     p.timeStamp=millis();
     delay(15);
     if(destination==0){
       p.destination = random(1,25);// bu 255 değer değişecek
     }else{
       p.destination = destination;
     }
     return p; //bu hatalı olabilir.
}

/*
 *write fonksiyoununda çağırılıyor. new passengerları gönderiyor. 
 */
char * createJsonString(){
  int listSize= newPassengers.count();
  aJsonObject *root = aJson.createObject();
  aJsonObject *passengerArray, *passengerJson;
  aJson.addNumberToObject(root,"tp",passengerQueue.count()); //tp = total passenger number in stataion
  aJson.addItemToObject(root, "pa", passengerArray = aJson.createArray());// pa = passengerArray
  int i=0;
  PASSENGER p = passengerQueue.pop();
  passengerJson=aJson.createObject();
  aJson.addNumberToObject(passengerJson,"d", p.destination);
  aJson.addNumberToObject(passengerJson,"s", p.src_station);
  aJson.addNumberToObject(passengerJson,"t", p.timeStamp);
  aJson.addItemToArray(passengerArray, passengerJson);
  return aJson.print(root);
}
//get passenger komutu alındığında passenger listesini gönderir.
char* st_passengerlist(int transferPassenger){
  /*if(transferPassenger > passengerQueue.count()){  
    transferPassenger=passengerQueue.count();
  }*/
    PASSENGER pas = passengerQueue.pop();
    aJsonObject *root = aJson.createObject();
    aJson.addNumberToObject(root,"nP",transferPassenger);
    aJson.addNumberToObject(root,"d", pas.destination);
    aJson.addNumberToObject(root,"s", pas.src_station);
    aJson.addNumberToObject(root,"t", pas.timeStamp);
    char* passList=aJson.print(root);
    aJson.deleteItem(root);
    coSend(ST_SE_PASSENGER_LIST,passList);
    //delay(100);
    //Serial.println(passList);
    return passList;
}

/*
 *Parses "json_String"
 */
void se_config(){
  json_String = (char*)mes.params;
  // TODO: Check json objects wheter null or not.
  aJsonObject* jsonObject = aJson.parse(json_String);
  aJsonObject* typeJson = aJson.getObjectItem(jsonObject, "type");
  type = typeJson -> valueint;
  
  if(type==1){
    aJsonObject* paramMin = aJson.getObjectItem(jsonObject, "minValue"); 
    aJsonObject* paramMax = aJson.getObjectItem(jsonObject, "maxValue");
    param_min=paramMin->valueint;
    param_max=paramMax->valueint;
    // TODO: Check json objects whether null or not.
  }else if(type==2){
    aJsonObject* paramMean = aJson.getObjectItem(jsonObject, "mean");
    aJsonObject* paramStdDev = aJson.getObjectItem(jsonObject, "standartDeviation");
    param_mean=paramMean->valuefloat;
    param_stdv=paramStdDev->valuefloat;
  }else if(type==3){
    aJsonObject* paramLambda = aJson.getObjectItem(jsonObject, "lambda");
    param_lambda=paramLambda->valuefloat;
  }else if(type==3){
    aJsonObject* paramProbability = aJson.getObjectItem(jsonObject, "probability");
    param_probability=paramProbability->valuefloat;
  }
}



void se_add_passenger(){
  json_String = (char*)mes.params;
  // TODO: Check json objects wheter null or not.
  aJsonObject* jsonObject = aJson.parse(json_String);
  aJsonObject* numJson = aJson.getObjectItem(jsonObject, "num");
  aJsonObject* dstJson = aJson.getObjectItem(jsonObject, "dst");
  numOfCreatedPassenger = numJson -> valueint; 
  dst = dstJson -> valueint;
}

void se_get_passenger(){
  json_String = (char*)mes.params;
  // TODO: Check json objects wheter null or not.
  aJsonObject* jsonObject = aJson.parse(json_String);
  aJsonObject* numJson = aJson.getObjectItem(jsonObject, "num");
  numOfGetPassenger = numJson -> valueint;
}
